﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Data;
using System.Collections;

//  Nati Nissim ID: 038589602 
//  Michael Biniashvili ID: 061669966


namespace SimulationHW2
{
    class CalculateTables
    {
        #region Properties

        public double varianceOfTable = 0;
        public double standartDiviation = 0;
        public double varianceOfModel = 0;
        public double fCalculated = 0;
        public double k1;
        public string[] strResult;
        public double sumResult;
        public double k2 = PropertiesSimulation.Level-1;

        #endregion Properties

        public CalculateTables()
        {
            InitMyTable();
        }

        #region Calculated T table

        private List<string> levelsT = new List<string>();
        private List<string> globalColumnsListT = new List<string>();
        private Hashtable columnsMappingT = new Hashtable();
        private string[] InitMyTableT()
        {
            globalColumnsListT.Add("t0");
            columnsMappingT.Add("t0", typeof(double));
            CreateLevels("t");
            FillColumnsNames("t");
            for (int i = 0; i < globalColumnsListT.Count; i++)
            {
                PropertiesSimulation.dataTableT.Columns.Add(new DataColumn(globalColumnsListT[i], (Type)columnsMappingT[globalColumnsListT[i]]));
            }

            for (int i = 0; i < 3; i++)
            {
                PropertiesSimulation.dataTableT.Rows.Add(new object[] { });
            }

            for (int i = 0; i < PropertiesSimulation.dataTableT.Rows.Count - 2; i++)
            {
                for (int g = 0; g < PropertiesSimulation.dataTableT.Columns.Count; g++)
                {
                    string colB = globalColumnsListB[g];
                    string colT = globalColumnsListT[g];
                    PropertiesSimulation.dataTableT.Rows[i][colT] = Trim(Math.Abs((double)PropertiesSimulation.dataTableB.Rows[i][colB]) / standartDiviation);
                }
            }

            int count = 0;
            for (int i = 0; i < PropertiesSimulation.dataTableT.Rows.Count - 2; i++)
            {
                for (int g = 0; g < PropertiesSimulation.dataTableT.Columns.Count; g++)
                {
                    string colT = globalColumnsListT[g];
                    string colB = globalColumnsListB[g];
                    if ((double)PropertiesSimulation.dataTableT.Rows[i][colT] > (double)PropertiesSimulation.Coefficient)
                    {
                        PropertiesSimulation.dataTableT.Rows[i + 1][colT] = 1;
                        count++;
                    }
                    else
                    {
                        PropertiesSimulation.dataTableT.Rows[i + 1][colT] = 0;
                    }
                    PropertiesSimulation.dataTableT.Rows[i + 2][colT] = Trim((double)PropertiesSimulation.dataTableB.Rows[i][colB] * (double)PropertiesSimulation.dataTableT.Rows[i + 1][colT]);
                    sumResult += (double)PropertiesSimulation.dataTableT.Rows[i + 2][colT];
                }
            }

            string[] strRegressionResult = new string[count];
            for (int i = 0; i < PropertiesSimulation.dataTableT.Rows.Count - 2; i++)
            {
                for (int g = 0, k = 0; g < PropertiesSimulation.dataTableT.Columns.Count; g++)
                {
                    string colT = globalColumnsListT[g];
                    string colX = globalColumnsList[g + 1];
                    if ((double)PropertiesSimulation.dataTableT.Rows[i + 2][colT] != 0)
                    {
                        strRegressionResult[k] = string.Format("{0}*({1})  ", (double)PropertiesSimulation.dataTableT.Rows[i + 2][colT], colX);
                        k++;
                    }
                }
            }
            
            return strRegressionResult;
        }

        #endregion Calculated T table

        #region Calculated B table

        private List<string> levelsB = new List<string>();
        private List<string> globalColumnsListB = new List<string>();
        private Hashtable columnsMappingB = new Hashtable();

        private void InitMyTableB()
        {
            globalColumnsListB.Add("b0");
            columnsMappingB.Add("b0", typeof(double));
            CreateLevels("b");
            FillColumnsNames("b");
            for (int i = 0; i < globalColumnsListB.Count; i++)
            {
                PropertiesSimulation.dataTableB.Columns.Add(new DataColumn(globalColumnsListB[i], (Type)columnsMappingB[globalColumnsListB[i]]));
            }

            for (int i = 0; i < 1; i++)
            {
                PropertiesSimulation.dataTableB.Rows.Add(new object[] { });
            }

            for (int i = 0; i < PropertiesSimulation.dataTableB.Rows.Count; i++)
            {
                for (int g = 0; g < PropertiesSimulation.dataTableB.Columns.Count; g++)
                {
                    double sumB = 0;
                    string col = globalColumnsListB[g];
                    string colAverageY = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - 4];
                    string colXdt = globalColumnsList[g + 1];
                    for (int dt = 0; dt < PropertiesSimulation.dataTable.Rows.Count; dt++)
                    {
                        sumB += ((double)(PropertiesSimulation.dataTable.Rows[dt][colXdt]) * (double)PropertiesSimulation.dataTable.Rows[dt][colAverageY]);
                    }
                    sumB = sumB / PropertiesSimulation.dataTable.Rows.Count;
                    PropertiesSimulation.dataTableB.Rows[i][col] = Trim(sumB);
                }
            }
        }

        #endregion Calculated B table

        #region Calculated  table Simulation

        private List<string> levels = new List<string>();
        private List<string> globalColumnsList = new List<string>();
        private Hashtable columnsMapping = new Hashtable();

        private void InitMyTable()
        {
            globalColumnsList.Add("Number");
            columnsMapping.Add("Number", typeof(int));
            globalColumnsList.Add("X0");
            columnsMapping.Add("X0", typeof(double));
            CreateLevels("X");
            // Create Half Columns to Simulation
            FillColumnsNames("x");

            CreateMiscColumns("Y");

            CreateMiscColumns("Average");

            CreateMiscColumns("Variance of Experiment J^2");

            CreateMiscColumns("Y^");

            CreateMiscColumns("(y^-y)^2");

            for (int i = 0; i < globalColumnsList.Count; i++)
            {
                PropertiesSimulation.dataTable.Columns.Add(new DataColumn(globalColumnsList[i], (Type)columnsMapping[globalColumnsList[i]]));
            }

            for (int i = 1; i <= Math.Pow((double)PropertiesSimulation.Degree, (double)PropertiesSimulation.Level); i++)
            {
                PropertiesSimulation.dataTable.Rows.Add(new object[] { i, 1 });
            }

            double average = 0;
            string colY = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - (PropertiesSimulation.Amount + 5)];
            string colAverage = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - 4];
            for (int i = 0; i < PropertiesSimulation.dataTable.Rows.Count; i++)
            {
                // Insert the part full Simulation table (x1 and x2 .....)
                string[] bin = GetIntBinaryString(i);
                for (int z = 0; z < levels.Count; z++)
                {
                    PropertiesSimulation.dataTable.Rows[i][levels[z]] = bin[z];
                }

                // Insert the part to Half Simulation table (x1*x2 and x1*x3......)
                if (PropertiesSimulation.IsPartialType)
                {
                    for (int g = 0; (g + 2 + levels.Count) <= PropertiesSimulation.dataTable.Columns.Count - (PropertiesSimulation.Amount + 6); g++)
                    {
                        string t = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - (PropertiesSimulation.Amount + 6)];
                        string col = globalColumnsList[2 + levels.Count + g];
                        string[] cols = col.Split('x');
                        double val = 1;
                        for (int j = 0; j < cols.Length; j++)
                        {
                            val *= (double)PropertiesSimulation.dataTable.Rows[i][cols[j]];
                        }
                        PropertiesSimulation.dataTable.Rows[i][col] = val;
                    }
                }
                // Insert Data array to data table
                PropertiesSimulation.dataTable.Rows[i][colY] = PropertiesSimulation.ArrayData[i];

                // Insert Data Y1 and Y2... to data table
                for (int yd = 1; yd <= PropertiesSimulation.Amount; yd++)
                {
                    string colYD = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - (PropertiesSimulation.Amount + 5) + yd];

                    if (i == PropertiesSimulation.CombinationId - 1)
                    {
                        PropertiesSimulation.dataTable.Rows[i][colYD] = PropertiesSimulation.ArrayDataY[yd - 1];
                    }
                    else PropertiesSimulation.dataTable.Rows[i][colYD] = 0;
                }

                // Calculate the Average Columns Y
                double avSum = 0;
                for (int sumAverage = 0; sumAverage < PropertiesSimulation.Amount + 1; sumAverage++)
                {
                    string colSumY = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - (PropertiesSimulation.Amount + 5) + sumAverage];
                    if (i == PropertiesSimulation.CombinationId - 1)
                    {
                        avSum += (double)(PropertiesSimulation.dataTable.Rows[i][colSumY]);
                    }
                }
                if (i == PropertiesSimulation.CombinationId - 1) PropertiesSimulation.dataTable.Rows[i][colAverage] = average = avSum / (PropertiesSimulation.Amount + 1);
                else PropertiesSimulation.dataTable.Rows[i][colAverage] = PropertiesSimulation.ArrayData[i];
            }

            // Calculate the Variance and Standard Diviation
            for (int i = 0; i < PropertiesSimulation.dataTable.Rows.Count; i++)
            {
                double sumVar = 0;
                string colVar = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - 3];

                for (int j = 0; j < PropertiesSimulation.Amount + 1; j++)
                {
                    string colYD = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - (PropertiesSimulation.Amount + 5) + j];
                    if (i == PropertiesSimulation.CombinationId - 1)
                    {
                        sumVar += Trim(Math.Pow((double)(PropertiesSimulation.dataTable.Rows[i][colYD]) - (average), 2));
                    }
                }
                if (i == PropertiesSimulation.CombinationId - 1)
                {
                    // Insert Variance to table Simulation
                    PropertiesSimulation.dataTable.Rows[i][colVar] = varianceOfTable = Trim(sumVar / (PropertiesSimulation.Amount));
                    // Insert Standard Diviation to table Simulation
                    standartDiviation = Trim(Math.Sqrt(varianceOfTable / Math.Pow(PropertiesSimulation.Degree, PropertiesSimulation.Level)));
                }
            }

            InitMyTableB();
            strResult = InitMyTableT();
            string varY = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - 2];
            for (int i = 0; i < PropertiesSimulation.dataTable.Rows.Count; i++)
            {
                // Calculate the Y^ from T table and the Simulation table  
                double sumY = 0;
                for (int g = 0; g < globalColumnsListT.Count; g++)
                {
                    string colX = globalColumnsList[g + 1];
                    string colT = globalColumnsListT[g];
                    sumY += Trim((double)PropertiesSimulation.dataTable.Rows[i][colX] * (double)PropertiesSimulation.dataTableT.Rows[2][colT]);
                }
                PropertiesSimulation.dataTable.Rows[i][varY] = Trim(sumY);
            }

            //  Calculate the columns (y^-y)^2 
            string colY_Y = globalColumnsList[PropertiesSimulation.dataTable.Columns.Count - 1];
            double sumVarianceOfModel = 0;
            double sumRejectAccept = 0;
            for (int i = 0; i < PropertiesSimulation.dataTable.Rows.Count; i++)
            {
                PropertiesSimulation.dataTable.Rows[i][colY_Y] = Trim(Math.Pow(Trim((double)PropertiesSimulation.dataTable.Rows[i][varY] - (double)PropertiesSimulation.dataTable.Rows[i][colAverage]), 2));
                sumVarianceOfModel += (double)PropertiesSimulation.dataTable.Rows[i][colY_Y];
            }

            // Calculate the Variance of Model
            for (int g = 0; g < globalColumnsListT.Count; g++)
            {
                string colT = globalColumnsListT[g];
                sumRejectAccept += Trim((double)PropertiesSimulation.dataTableT.Rows[1][colT]);
            }
            varianceOfModel = Trim(sumVarianceOfModel / (k1 = PropertiesSimulation.dataTable.Rows.Count - sumRejectAccept));

            fCalculated = Trim(varianceOfModel / varianceOfTable);


        }

        #endregion Calculated table Simulation

        #region Functions help Simulation
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private double Trim(double value)
        {
            return Math.Round(value, PropertiesSimulation.Epsilon);
        }

        #region Columns 
        public static List<List<string>> Combination(List<string> values, int size)
        {
            if (size == 0 || values.Count == 0) return new List<List<string>> { new List<string>() };

            List<List<string>> combination = new List<List<string>>();

            var iter = values.GetEnumerator();
            iter.MoveNext();
            string actual = iter.Current;

            List<string> subSet = new List<string>(values);
            subSet.Remove(actual);

            List<List<string>> subSetCombination = Combination(subSet, size);

            foreach (List<string> set in subSetCombination)
            {
                List<string> newSet = new List<string>(set);
                newSet.Add(actual);
                combination.Add(newSet);
            }

            combination.AddRange(Combination(subSet, size));

            return combination;
        }

        private List<string> GenerateColumnsNames(string tableName)
        {
            List<List<string>> powerSet = new List<List<string>>();
            if (tableName == "b")
                powerSet.AddRange((Combination(levelsB.ToList(), levelsB.Count)));
            else if (tableName == "t")
                powerSet.AddRange((Combination(levelsT.ToList(), levelsT.Count)));
            else //x
                powerSet.AddRange((Combination(levels.ToList(), levels.Count)));

            List<List<string>> sortedList = new List<List<string>>();

            for (int i = 1; i <= levels.Count; i++)
            {
                foreach (List<string> list in powerSet)
                {
                    if (list.Count == i) sortedList.Add(list);
                }
            }

            powerSet = sortedList;
            List<string> result = new List<string>();
            foreach (List<string> sub in powerSet)
            {
                StringBuilder sb = new StringBuilder();
                for (int index = sub.Count - 1; index >= 0; index--)
                {
                    string s = sub[index];

                    if (sub.Count > 1 && index != 0)
                        sb.Append(s + "x");
                    else
                        sb.Append(s);
                }
                result.Add(sb.ToString());
            }

            if (!PropertiesSimulation.IsPartialType) return result.GetRange(0, this.levels.Count);
            return result;
        }

        #endregion Columns
        private void FillColumnsNames(string x)
        {
            List<string> columnsNames = GenerateColumnsNames(x);

            foreach (string columnsName in columnsNames)
            {
                switch (x)
                {
                    case "x":
                        if (globalColumnsList.Contains(columnsName)) continue;
                        globalColumnsList.Add(columnsName);
                        columnsMapping.Add(columnsName, typeof(double));
                        break;
                    case "b":
                        if (globalColumnsListB.Contains(columnsName)) continue;
                        globalColumnsListB.Add(columnsName);
                        columnsMappingB.Add(columnsName, typeof(double));
                        break;
                    case "t":
                        if (globalColumnsListT.Contains(columnsName)) continue;
                        globalColumnsListT.Add(columnsName);
                        columnsMappingT.Add(columnsName, typeof(double));
                        break;
                }
            }
        }

        private void CreateLevels(string bt)
        {
            for (int i = 1; i <= PropertiesSimulation.Level; i++)
            {

                string res = string.Format("{0}{1}", bt, i);
                switch (bt)
                {
                    case "X":
                        levels.Add(res);
                        break;
                    case "b":
                        levelsB.Add(res);
                        break;
                    case "t":
                        levelsT.Add(res);
                        break;
                }
            }
        }

        string[] GetIntBinaryString(int n)
        {
            string[] sb = new string[PropertiesSimulation.Level];
            int pos = PropertiesSimulation.Level - 1;
            int i = 0;

            while (i < PropertiesSimulation.Level)
            {
                if ((n & (1 << i)) != 0)
                {
                    sb[pos] = "1";
                }
                else
                {
                    sb[pos] = "-1";
                }
                pos--;
                i++;
            }
            return sb;
        }

        private void CreateMiscColumns(string xy)
        {
            switch (xy)
            {
                case "Y":
                    for (int i = 1; i <= PropertiesSimulation.Amount + 1; i++)
                    {
                        string res = string.Format("{0}{1}", xy, i);
                        globalColumnsList.Add(res);
                        columnsMapping.Add(res, (typeof(double)));
                    }
                    break;
                case "Average":
                    string resAverage = "Y Average";
                    globalColumnsList.Add(resAverage);
                    columnsMapping.Add(resAverage, typeof(double));
                    break;
                case "Variance of Experiment J^2":
                case "Y^":
                case "(y^-y)^2":
                    globalColumnsList.Add(xy);
                    columnsMapping.Add(xy, typeof(double));
                    break;
            }
        }


        private void CreateColumns(string variance)
        {
            globalColumnsList.Add(variance);
            columnsMapping.Add(variance, typeof (double));
        }

        #endregion Functions help Simulation
    }
}
